//---------------------------------------------------------------------------
// Process Class
// Author : Woo-Hyun Kim
// Email : woorung@icu.ac.kr
// Date : Jul. 7, 2006
//---------------------------------------------------------------------------
#include "CProcess.h"
//---------------------------------------------------------------------------
// static global parameters for signal handling
//CSignal *CSignal::_instance = NULL;
//CSignalHandler *CSignal::_signal_handlers[NSIG];
//---------------------------------------------------------------------------
CProcess::CProcess()
{
	status = -1;

	pid = -1;
	ppid = -1;
	cpid[CHILD_PROC_1] = -1; // 1st child process before run
	cpid[CHILD_PROC_2] = -1; // 2nd child process after run

	// child to parent
	up_pipefd[0] = -1;
	up_pipefd[1] = -1;

	// parent to child
	down_pipefd[0] = -1;
	down_pipefd[1] = -1;

	// child to parent
	err_pipefd[0] = -1;
	err_pipefd[1] = -1;

	verbose = 0;
}
//--------------------------------------------------------------------------
CProcess::CProcess(int verbose)
{
	status = -1;

	pid = -1;
	ppid = -1;
	cpid[CHILD_PROC_1] = -1; // 1st child process before run
	cpid[CHILD_PROC_2] = -1; // 2nd child process after run
	
	// child to parent
	up_pipefd[0] = -1;
	up_pipefd[1] = -1;

	// parent to child
	down_pipefd[0] = -1;
	down_pipefd[1] = -1;

	// child to parent
	err_pipefd[0] = -1;
	err_pipefd[1] = -1;

	this->verbose = verbose;
}
//--------------------------------------------------------------------------
CProcess::~CProcess()
{
	if(up_pipefd[0]>0) close(up_pipefd[0]);
	if(up_pipefd[1]>0) close(up_pipefd[1]);

	if(down_pipefd[0]>0) close(down_pipefd[0]);
	if(down_pipefd[1]>0) close(down_pipefd[1]);

	if(err_pipefd[0]>0) close(err_pipefd[0]);
	if(err_pipefd[1]>0) close(err_pipefd[1]);
}
//--------------------------------------------------------------------------
void CProcess::before_run()
{
	// override the user-defined code
}
//--------------------------------------------------------------------------
void CProcess::run()
{
	// override the user-defined code
	cerr << "###################################################### /##################" << endl;
	cerr << "######    ##################/#######/#######/   ###### ###################" << endl;
	cerr << "##### #### #  ###  ##     ###     ###     ########     ####    ###########" << endl;
	cerr << "##### #### #  ##  ##  ###  ##  ##  ##  ##  ##  ##  ##  ##  ###  ##########" << endl;
	cerr << "##### #### ##  # ###  ___####  ######  ######  ## ###  ##  ___############" << endl;
	cerr << "######    ####  ####@      ##  ######  ######   ##    ###@      ##########" << endl;
	cerr << "##########################################################################" << endl;
}
//--------------------------------------------------------------------------
void CProcess::after_run()
{
	// override the user-defined code
}
//--------------------------------------------------------------------------
pid_t CProcess::get_pid()
{
	return pid;
}
//--------------------------------------------------------------------------
// only valid in parent process
//--------------------------------------------------------------------------
pid_t CProcess::get_cpid(int i)
{
	// only valid in parent process
	return cpid[i];
}
//--------------------------------------------------------------------------
pid_t CProcess::get_ppid()
{
	return ppid;
}
//--------------------------------------------------------------------------
int CProcess::get_status()
{
	return status;
}
//--------------------------------------------------------------------------
// child write to parent
//--------------------------------------------------------------------------
size_t CProcess::write_upstream(const void *buf, size_t size)
{
	return write(up_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// child write to parent
//--------------------------------------------------------------------------
size_t CProcess::write_upstream(const char *buf)
{
	size_t size = strlen(buf);

	return write(up_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// parent read from child 
// # Attention : it must be overridden in subclasses
//--------------------------------------------------------------------------
int CProcess::read_upstream(const char *buf)
{
	cout << buf;

	return 1;
}
//--------------------------------------------------------------------------
int CProcess::read_upstream(const void *buf, size_t size)
{
	for(size_t i=0;i<size;i++) cout << *((char *)buf + i);

	return 1;
}
//--------------------------------------------------------------------------
// parent write child 
//--------------------------------------------------------------------------
size_t CProcess::write_downstream(const void *buf, size_t size)
{
	return write(down_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// parent write child 
//--------------------------------------------------------------------------
size_t CProcess::write_downstream(const char *buf)
{
	size_t size = strlen(buf);

	return write(down_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// child read from parent
// # Attention : it must be overridden in subclasses
//--------------------------------------------------------------------------
int CProcess::read_downstream(const char *buf)
{
	cout << buf;
	
	return 1;
}
//--------------------------------------------------------------------------
int CProcess::read_downstream(const void *buf, size_t size)
{
	for(size_t i=0;i<size;i++) cout << *((char *)buf + i);

	return 1;
}
//--------------------------------------------------------------------------
// child write to parent
//--------------------------------------------------------------------------
size_t CProcess::write_errstream(const void *buf, size_t size)
{
	return write(err_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// child write to parent
//--------------------------------------------------------------------------
size_t CProcess::write_errstream(const char *buf)
{
	size_t size = strlen(buf);

	return write(err_pipefd[1], buf, size);
}
//--------------------------------------------------------------------------
// parent read from child 
// # Attention : it must be overridden in subclasses
//--------------------------------------------------------------------------
int CProcess::read_errstream(const char *buf)
{
	cerr << buf;

	return 1;
}
//--------------------------------------------------------------------------
int CProcess::read_errstream(const void *buf, size_t size)
{
	for(size_t i=0;i<size;i++) cerr << *((char *)buf + i);

	return 1;
}
//--------------------------------------------------------------------------
// close downstream pipe from parent to child
//--------------------------------------------------------------------------
int CProcess::close_downstream()
{
	char ch = EOF;

	write_downstream(&ch, 1);
}
//--------------------------------------------------------------------------
// close upstream pipe from child to parent
//--------------------------------------------------------------------------
int CProcess::close_upstream()
{
	char ch = EOF;

	write_upstream(&ch, 1);
}
//--------------------------------------------------------------------------
// close errstream pipe from child to parent
//--------------------------------------------------------------------------
int CProcess::close_errstream()
{
	char ch = EOF;

	write_errstream(&ch, 1);
}
//--------------------------------------------------------------------------
void CProcess::start()
{
	// for signal handling
	//CSignal::instance()->register_handler(SIGHUP, &sighup_handler);
	//CSignal::instance()->register_handler(SIGINT, &sigint_handler);
	//CSignal::instance()->register_handler(SIGQUIT, &sigquit_handler);

	CSignalHandler *old = CSignal::instance()->register_handler(SIGCHLD, &sigchld_handler);
	if(old!=NULL) {
		if(verbose) cerr << "[p]# signal handler on SIGCHLD is changed" << endl;
	}
	else {
		if(verbose) cerr << "[p]# signal handler on SIGCHLD is NOT changed" << endl;
	}

	// create pipes
	if(pipe(up_pipefd)<0||pipe(down_pipefd)<0||pipe(err_pipefd)<0) {
		if(verbose) cerr << "##### p(" << getpid() << ") failed to pipe()" << endl;
		exit(-1);
	}

	pid = fork();

	if(pid==-1) {
		if(verbose) cerr << "##### p(" << getpid() << ") failed to fork()" << endl;
		exit(-1);
	}
	// 2nd child process for user process
	else if(pid==0) { 
		if(verbose) cerr << "[c2]# p(" << getppid() << ") => c2(" << getpid() << ")" << endl;

		// remove signal handler
		CSignal::instance()->remove_handler(SIGCHLD);
		// parent first, child next
		//---------------------------------------------------------------
		CSIGUSR2Handler sigusr2_handler;
		CSignalHandler *old = CSignal::instance()->register_handler(SIGUSR2, &sigusr2_handler);
		if(old!=NULL) {
			if(verbose) cerr << "[c2]# signal handler on SIGUSR2 is changed" << endl;
		}
		else {
			if(verbose) cerr << "[c2]# signal handler on SIGUSR2 is NOT changed" << endl;
		}
		if(verbose) cerr << "[c2]# c2(" << getpid() << ") waits for p(" << getppid() << ")'s start" << endl;
		while(sigusr2_handler.parent_started()==0) sleep(3);
		if(verbose) cerr << "[c2]# c2(" << getpid() << ") finally starts" << endl;
		CSignal::instance()->remove_handler(SIGUSR2);
		//---------------------------------------------------------------

		// ### use of read downstream & write upstream
		// so, no use of read upstream
		close(up_pipefd[0]);
		up_pipefd[0] = -1;
		// also, no use of write downstream
		close(down_pipefd[1]);
		down_pipefd[1] = -1;
		// also, no use of read errstream
		close(err_pipefd[0]);
		err_pipefd[0] = -1;

		pid = getpid(); // because pid is 0, the 2nd pid must be got
		ppid = getppid();

		// down_pipefd[0] to stdin
		dup2(down_pipefd[0],0);
		// up_pipefd[1] to stdout
		dup2(up_pipefd[1],1);
		dup2(err_pipefd[1],2); // stderr

		//__run();
		if(verbose) cerr << "[c2]# c2(" << getpid() << ") runs user process" << endl;
		run();

		close_upstream();
		close_errstream();

		if(verbose) cerr << "[c2]# c2(" << getpid() << ") is finally finished" << endl;
		exit(0);
	}
	else {
		// parent save the 2nd child process for user process
		cpid[CHILD_PROC_2] = pid; // run() itself

		int another_pid = fork();
		if(another_pid==-1) {
			if(verbose) cerr << "##### p(" << getpid() << ") failed to another fork()" << endl;
			exit(-1);
		}
		// 1st child process for filtering input
		else if(another_pid==0) {
			if(verbose) cerr << "[c1]# p(" << getppid() << ") => c1(" << getpid() << ")" << endl;

			// remove signal handler
			CSignal::instance()->remove_handler(SIGCHLD);
			// parent first, child next
			//---------------------------------------------------------------
			CSIGUSR2Handler sigusr2_handler;
			CSignalHandler *old = CSignal::instance()->register_handler(SIGUSR2, &sigusr2_handler);
			if(old!=NULL) {
				if(verbose) cerr << "[c1]# signal handler on SIGUSR2 is changed" << endl;
			}
			else {
				if(verbose) cerr << "[c1]# signal handler on SIGUSR2 is NOT changed" << endl;
			}
			if(verbose) cerr << "[c1]# c1(" << getpid() << ") waits for p(" << getppid() << ")'s start" << endl;
			while(sigusr2_handler.parent_started()==0) sleep(3);
			if(verbose) cerr << "[c1]# c1(" << getpid() << ") finally starts" << endl;
			CSignal::instance()->remove_handler(SIGUSR2);
			//---------------------------------------------------------------

			// ### use of only write downstream
			// so, no use of read/write upstream
			close(up_pipefd[0]);
			up_pipefd[0] = -1;
			close(up_pipefd[1]);
			up_pipefd[1] = -1;
			// also, no use of read downstream
			close(down_pipefd[0]);
			down_pipefd[0] = -1;
			// also, no use of err downstream
			close(err_pipefd[0]);
			err_pipefd[0] = -1;
			close(err_pipefd[1]);
			err_pipefd[1] = -1;

			pid = getpid(); // because pid is 0, the 1st child pid must be got
			ppid = getppid();

			// child process to achieve downstream 
			if(verbose) cerr << "[c1]# c1(" << getpid() << ") runs before_run()" << endl;
			before_run();
			// close downstream pipe from parent to childw 
			close_downstream();

			if(verbose) cerr << "[c1]# c1(" << getpid() << ") is finally finished" << endl;
			exit(0);
		}
		// parent process for forwarding output
		else {
			// parent save the 2nd child process for user process
			cpid[CHILD_PROC_1] = another_pid;

			// ### use of only read upstream
			// so, no use of read/write downstream
			close(down_pipefd[0]);
			down_pipefd[0] = -1;
			close(down_pipefd[1]);
			down_pipefd[1] = -1;
			// also, no use of write upstream
			close(up_pipefd[1]);
			up_pipefd[1] = -1;
			// also, no use of write errstream
			close(err_pipefd[1]);
			err_pipefd[1] = -1;

			ppid = getppid();

			// hook the SIGCHLD signal of child processes
			sigchld_handler.set_chld1(cpid[CHILD_PROC_1]);
			sigchld_handler.set_chld2(cpid[CHILD_PROC_2]);

			if(verbose) cerr << "[p]# p(" << getpid() << ") makes c1(" << cpid[CHILD_PROC_1] << ") & c2(" << cpid[CHILD_PROC_2] << ") running" << endl;
			// parent first, child next
			//-----------------------------------
			kill(cpid[CHILD_PROC_1], SIGUSR2);
			kill(cpid[CHILD_PROC_2], SIGUSR2);
			//-----------------------------------

			if(verbose) cerr << "[p]# p(" << getpid() << ") is freezed until c1(" << cpid[CHILD_PROC_1] << ")'s finish" << endl;
			while(sigchld_handler.exit_chld1()==0) sleep(30);
			//if(verbose) cerr << "[p]# c1(" << cpid[CHILD_PROC_1] << ") was successfully finished" << endl;
		}
	}
}
//--------------------------------------------------------------------------
int CProcess::waits()
{
	if(verbose) cerr << "[p]# p(" << getpid() << ") is freezed until c2(" << cpid[CHILD_PROC_2] << ")'s finish" << endl;

	pid_t wpid;
	//fd_set reads, writes, temp_read, temp_write;
	fd_set reads, temp_read;
	int fd_max;
	struct timeval timeout;
	int select_result;

	int size = 0;
	char buf[READ_BUF_SIZE];
	int retry = 0;
	// non-blocking I/O & wait ---------------
	do {
		FD_ZERO(&reads);
		//FD_ZERO(&writes);
		FD_SET(up_pipefd[0], &reads);
		FD_SET(err_pipefd[0], &reads); // for stderr
		//FD_SET(down_pipefd[1], &writes);
		//fd_max = max(up_pipefd[0],down_pipefd[1]);
		fd_max = max(up_pipefd[0], err_pipefd[0]);

		temp_read = reads;
		//temp_write = writes;
		timeout.tv_sec = 30;
		timeout.tv_usec = 0;

		select_result = select(fd_max+1, &temp_read, (fd_set *)0, (fd_set *)0, &timeout);
		if(select_result==-1) { // select error
			// in case of massive data sort, select() might be interrupted by some signals due to slow system calls
			// so, select() must be retried
			if(errno==EINTR) {
				if(verbose) cerr << "[p]# p(" << getpid() << ") failed by interruption in select(), but retry it (retry=" << retry << ")" << endl;

				retry++;
			}
			else {
				if(verbose) cerr << "##### p(" << getpid() << ") failed in select() with errno = " << errno << endl;

				exit(-1);
			}
		}
		else if(select_result==0) { // timeout
			if(verbose) cerr << "[p]# p(" << getpid() << ") select() timeout" << endl;
		}
		else {
			retry = 0;

			memset((char *)buf, '\0', sizeof(buf));

			// parent read data from child
			if(FD_ISSET(up_pipefd[0], &temp_read)) {
				if((size=read(up_pipefd[0], buf, sizeof(buf)-1))>0) {
					buf[size] = '\0';

					// callback user read function
					if(size>0) read_upstream(buf);

					//memset((char *)buf, '\0', sizeof(buf));
				}
			}
			if(FD_ISSET(err_pipefd[0], &temp_read)) {
				if((size=read(err_pipefd[0], buf, sizeof(buf)-1))>0) {
					buf[size] = '\0';
					if(size>0) read_errstream(buf);
				}
			}
			// parent write data to child
/*
			else if(FD_ISSET(down_pipefd[1], &temp_write)) {
				// nothing	
			}
*/
		}
	} while(sigchld_handler.exit_chld2()!=1); // continue until SIGCHLD

	// read the rest of stdout
	while((size=read(up_pipefd[0], buf, sizeof(buf)-1))>0) {
		buf[size] = '\0';
		if(size>0) read_upstream(buf);
	}

	// read the rest of stderr
	while((size=read(err_pipefd[0], buf, sizeof(buf)-1))>0) {
		buf[size] = '\0';
		if(size>0) read_errstream(buf);
	}

	wpid = sigchld_handler.get_chld2();
	status = sigchld_handler.get_chld2_status();

	if(wpid==-1) {
		if(verbose) cerr << "##### p(" << getpid() << ") failed to wait() : errnum = " << wpid << endl;
		exit(-1);
	}
	else {
		if(WIFEXITED(status)) {
			// normal exit
			if(verbose) cerr << "[p]# p(" << getpid() << ") successfully received the exit status " << WEXITSTATUS(status) << " from c(" << wpid << ")" << endl;
			return WEXITSTATUS(status);
		}
		else if(WIFSIGNALED(status)) {
			// abort, kill or signal
		 	if(verbose) cerr << "[p]# c(" << wpid << ") was finished with signal = " << WTERMSIG(status) << (WCOREDUMP(status) ? " with core file." : "") << endl;
			return -1;
		}
		else {
			if(verbose) cerr << "[p]# c(" << wpid << ") is stopped" << endl;
			return -1;
		}
	}

	return 1;
}
//--------------------------------------------------------------------------
